/*->c.macros */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"


#include "h.def"
#include "h.wos"
#include "h.pr"
#include "h.main"
#include "h.mym"

#include "h.file"
#include "h.script"
#include "h.ram"

#include "h.vtkey"

#include "h.record"

#include "h.xext"

#include "h.key"



/*****************************************************************************/
/* code for macros windows */


                        /* step between each macro entry in window */
#define MACDY   56
                        /* step between top of window and first macro */
#define MACFY   12
                        /* height of macro definitions */
#define MACHY   48

#define MACFX    8
#define MACSX    12*16+MACFX+8

#define MACSTRW  48


#define MACFXW   12*16
#define MACSXW   16*MACSTRW

#define MACWIDTH 1024




       macro * macros;        /* holds header for each macro */
       char  * mstrings;      /* holds macro strings */


static int  macrorecord;      /* global, are we recording a macro */
       int  macrostot;        /* number of macros defined */
static int  macroselect;      /* number of selected macro */


/*****************************************************************************/

/* find macro, by type or name */
/* return -1 if not found      */


int findmacro(char * name,int type)
{
 int i;

 for(i=0;i<macrostot;i++)
 {
  if(name)
  {
   if(!cstrcmp(macros[i].name,name)) return(i);
  }
  else
  if(type==macros[i].type) return(i);
 }

 return(-1);
}



/* lo level delete macro number n */

static void delmacron(int n)
{
 int     len;
 int     offset;
 int     moffset;
 int     i;

 if(n>=0 && n<macrostot)
 {
  offset=macros[n].defn;
  moffset=n*sizeof(macro);
  len=strlen(mstrings+offset);
  flex_midextend((flex_ptr)&mstrings,offset+len+1,-(len+1));
  flex_midextend((flex_ptr)&macros,moffset+sizeof(macro),-sizeof(macro));
  macrostot--;

  for(i=0;i<macrostot;i++)
  {
   if(macros[i].defn>offset) macros[i].defn-=len+1;
  }
 }
}





static int updatemacro(int mn,char * definition)
{
 int oldlen;
 int newlen;
 int delta;
 int offset;
 int i;

 newlen=strlen(definition)+1;
 offset=macros[mn].defn;
 oldlen=strlen(mstrings+offset)+1;
 delta=newlen-oldlen;
 if(flex_midextend((flex_ptr)&mstrings,offset+oldlen,delta))
 {
  strcpy(macros[mn].defn+mstrings,definition);
  for(i=0;i<macrostot;i++)
  {
   if(macros[i].defn>offset) macros[i].defn+=delta;
  }
  return(mn);
 }
 else
  return(-1);
}


static int addmacro(char * name,char * definition,int type)
{
 int mn=findmacro(name,type);
 int oldlen;
 int delta;

 if(mn>-1)      /* this macro exists */
 {
  return(updatemacro(mn,definition));
 }
 else           /* creating a new one */
 {
  if(flex_extend((flex_ptr)&macros,(macrostot+1)*sizeof(macro)))
  {
   delta=strlen(definition)+1;
   oldlen=flex_size((flex_ptr)&mstrings);

   if(flex_extend((flex_ptr)&mstrings,oldlen+delta))
   {
    mn=macrostot;
    macros[macrostot].type=type;
    strcpy(macros[macrostot].name,name);
    macros[macrostot].flags=0;


    macros[macrostot].defn=oldlen;
    strcpy(macros[macrostot].defn+mstrings,definition);
    macrostot++;
   }
   else
    return(-1);
  }
  else
   return(-1);
 }

 return(mn);
}



void macrosboot(void)
{
 flex_alloc((flex_ptr)&macros,0);
 flex_alloc((flex_ptr)&mstrings,0);
 macrostot=0;
 os3keypatch();
}


/*****************************************************************************/
/* code for handling macro windows */


/* redraw macros pane */

void macrosredrawsub(wimp_redrawstr * redrawstr,int more)
{
 int oy;
 int ox;
 int ylo;
 int yhi;
 int n;
 int y;
 int xhi;


 wimpfontstart();

 while(more)
 {
  ox=redrawstr->box.x0-redrawstr->scx;
  oy=redrawstr->box.y1-redrawstr->scy;

  if((redrawstr->g.x1-redrawstr->box.x0)>(MACSX+MACSXW))
  {
   xhi=redrawstr->box.x0+MACSX+MACSXW;
   bbc_gwindow(redrawstr->g.x0,redrawstr->g.y0,xhi,redrawstr->g.y1-deltay);
  }
  else xhi=redrawstr->g.x1;

  if(redrawstr->g.x0<xhi)
  {
   ylo=-(redrawstr->g.y1-oy);            /* small number, top of window */
   yhi=-(redrawstr->g.y0-oy);            /* bigger, bottom of window */


   n=(ylo-MACFY)/MACDY;
   if(n<0) n=0;
   y=n*MACDY+MACFY;

   while(y<=(yhi+MACFY) && n<macrostot)
   {
    plinthtext(ox+MACFX,oy-y,MACFXW,MACHY,macros[n].name,(1<<8)|7);

    if(n!=macroselect)
    {
     noplinthtext(ox+MACSX,oy-y,MACSXW,MACHY,
                                        mstrings+macros[n].defn,(0<<8)|7);
    }
    else
    {
     noplinthtext(ox+MACSX,oy-y,MACSXW,MACHY,"",(0<<8)|7);
    }

    y+=MACDY;
    n++;
   } 
  }
  wimp_get_rectangle(redrawstr,&more);
 }

 wimpfontend();
}




void macrosredraw(void)
{
 wimp_redrawstr redrawstr;
 int            more;
 redrawstr.w=ewindow;
 wimp_redraw_wind(&redrawstr,&more);
 macrosredrawsub(&redrawstr,more);
}





void macrosrefreshentry(int n)
{
 wimp_redrawstr r;
 int            handle=whandle[MACROS];
 int            more;

 r.box.x0=0;
 r.box.x1=MACWIDTH;
 r.box.y1=/*-MACFY*/-n*MACDY+deltay;
 r.box.y0=r.box.y1-MACHY-deltay-MACFY;
 r.w=handle;

 wimp_update_wind(&r,&more);
 macrosredrawsub(&r,more);
}





/* scroll window to show entry */

static void displaymacro(int n)
{
 int yhi=-MACFY-n*MACDY;
 int ylo=-MACFY-(n+1)*MACDY;
 int wlo;
 int whi;

 getw(whandle[MACROS]);

 whi=by-y1;
 wlo=by-y0;

 if(ylo<wlo || yhi>whi)
   openatscroll(whandle[MACROS],0,-MACFY-n*MACDY-MACDY/2+(y1-y0)/2);
}





static void macrosextent(void)
{
 int maxx;
 int miny;
 int sety;

 getw(whandle[MACROS]);

 maxx=x1-x0;
 miny=y1-y0;

 sety=MACFY+MACDY*macrostot;
 if(sety>miny) miny=sety;

 extent(whandle[MACROS],0,-miny,maxx,0);
}





/* takes the selected macro, and stashes it */

static void macrosstash(void)
{
 if(macroselect>=0) updatemacro(macroselect,iconaddr(whandle[MACROS],0));
}


/* we use this to put an icon over the given macro        */
/* n can be -1 in which case the icon goes somewhere safe */

static void macrosselectone(int n)
{
 wimp_icon       isblock;
 wimp_icreate    iblock;
 int             icon=0;
 int             window=whandle[MACROS];

 wimp_get_icon_info(window,icon,&isblock);

 if(n>=0) strcpy(isblock.data.indirecttext.buffer,mstrings+macros[n].defn);
 else     n=-10;

 wimp_delete_icon(window,icon);

 isblock.box.y1=-MACFY-n*MACDY;
 isblock.box.y0=isblock.box.y1-MACHY+deltay;
 isblock.box.x0=MACSX+deltax;
 isblock.box.x1=isblock.box.x0+MACSXW-deltax;

 iblock.w=window;
 iblock.i=isblock;
 wimp_create_icon(&iblock,&icon);

 if(n>=0) iecarrot(window,icon);
}



static void macrosnewselect(int n)
{
 int oldn;

 oldn=macroselect;
 macrosstash();
 macrosselectone(n);
 macroselect=n;
 macrosrefreshentry(n);
 if(oldn>=0) macrosrefreshentry(oldn);
}



static void macrosnewselectx(int n)
{
 macrosnewselect(n);
 displaymacro(n);
}




static int macrosaddwindow(char * name,char * defn,int select)
{
 int type;
 int n;

 if(!*name) return(0);          /* no name - ignore */

 type=keytype(name);
 n=addmacro(name,defn,type);

 if(n<0) return(0);
 else
 {
  if(whandle[MACROS])
  {
   if(!select && n==macroselect)
   {
    macrosselectone(-1);
    macroselect=-1;
   }
   macrosextent();
   zapmenu();
   macrosrefreshentry(n);
   if(select) macrosnewselectx(n);
  }
  return(1);
 }
}




/* key presses */

void macroskey(int * key)
{
 int ch=*key;

 switch(ch)
 {
  case   CUP:
             if(macroselect>0) macrosnewselectx(macroselect-1);
             break;

  case CDOWN:
  case    CR:
             if(macroselect<(macrostot-1)) macrosnewselectx(macroselect+1);
             break;

     default:
             return;
             break;
 }

 *key=-1;
}




/* removes a macro definition */

static void macrodelete(void)
{
 if(macroselect>=0)
 {
  delmacron(macroselect);
  macrosextent();
  if(macroselect>=macrostot) macroselect=macrostot-1;
  macrosselectone(macroselect);
  refreshwindow(whandle[MACROS]);
 }
}




static void setpopmacros(void)
{
 unshadest(macros_menu,1,macroselect>=0);
 unshadest(macros_menu,2,macroselect>=0);
 unshadest(macros_menu,3,macroselect>=0);
 unshadest(macros_menu,4,macroselect>=0);
 sprintf(menuaddr(macros_menu,2),"Del. '%s'",
                                 macroselect>=0?macros[macroselect].name:"");



 tickst(macros_menu,4,macrorecord);
}





void macrosdecode(int m2,int m3,int m4,int m5)
{
 char temp[256];

 switch(m2)
 {
  case 0:                /* new */
         if(m3==0) macrosaddwindow(menuaddr(macrosname_menu,0),"",1);
         break;

  case 1:                /* copy */
         if(m3==0) 
         if(macroselect>=0)
         {
          macrosstash();
          strcpy(temp,mstrings+macros[macroselect].defn);
          macrosaddwindow(menuaddr(macrosname_menu,0),temp,1);
         }
         break;

  case 2:                /* delete */
         macrodelete();
         break;

  case 3:
         macrosstash();
         expandamacro(cedsendkey,macroselect);
         break;

  case 4:                /* record */
         macrosstash();
         macrorecord^=1;
         break;

  case 5:
         delallmacros(0);
         break;

  case 7:
         macrosstash();
         savedefaultmacros();
         break;
 }

 setpopmacros();
 m3=m4=m5;
}



static void macrospop(void)
{
 setpopmacros();
 popmenu(macros_menu);
}



/* click on macros pane */

void macrosicon(void)
{
 int n;

 if(buttons==2)
 {
  macrospop();
 }
 else
 if(buttons==4)
 {
  getw(whandle[MACROS]);

  n=(by-mousey-MACFY)/MACDY;
  if(n<0 || n>=macrostot) return;
  macrosnewselect(n);
 }
}



void macrosc(int gain)
{
 if(!gain) macrosstash();
}


/* close macros window */

void macrosclose(void)
{
 macrosstash();
 closedownt(MACROS);
 macrorecord=0;
}



/* click on menu, display macro window */

void macrosshow(void)
{
 int handle;

 if(whandle[MACROS])
 {
  forward(whandle[MACROS]);
 }
 else
 {
  handle=createwindow(MACROS);
  if(!handle) return;
  macrosextent();
  macroselect=-1;
  popup(handle,0);
 }
}



/* passed key presses, might like to record them */
/* return number of macro added to if any */

int macrorecorder(int ch)
{
 char string[12];

 if((macrorecord && macroselect>=0) || recordflag)
 {
  if(ch<160) uncode(string,ch);
  else
  if(ch<256)
  {
   string[0]=ch;
   string[1]=0;
  }
  else
  {
   sprintf(string,"{%s}",keystring(ch)); 
  }

  if(recordflag)
  {
   recordkeypress(string);
   return(-1);
  }
  else
  {
   strcat(iconaddr(whandle[MACROS],0),string);
   macrosrefreshentry(macroselect);
   return(macroselect);
  }
 }
 return(-1);
}




int macrossave(char * filename)
{
 FILE * fp;
 int    i;
 char   scrap[260];

 macrosstash();

 fp=ropen(filename,"wb");

 if(fp)
 {
  hsystdheader(fp,leaf(filename));

  rfprintf(fp,"void main(void)\n{\n");

/*  rfprintf(fp,"delallmacros();\n"); */

  for(i=0;i<macrostot;i++)
  {
   rfprintf(fp,"defmacro(\"%s\",\"%s\");\n",macros[i].name,
    unquotec(macros[i].defn+mstrings,scrap));
  }

  rfprintf(fp,"}\n\n");
  rclose(fp);
  savetypeclose(fp,filename);
 }
 return(1);
}



void savedefaultmacros(void)
{
 char string[256];

 saveftype=SCRIPT;
 sprintf(string,"%s.!Macros",path(AUTOP));

 macrossave(string);
}



/* trash all macros */

void delallmacros(int fp)
{
 flex_extend((flex_ptr)&macros,0);
 flex_extend((flex_ptr)&mstrings,0);
 macrostot=0;
 if(whandle[MACROS])
 {
  if(macroselect>=0) macrosselectone(-1);
  macroselect=-1;
  refreshwindow(whandle[MACROS]);
 }
 fp=0;
}



/* delete a named macro */

void delmacro(int fp)
{
 char * p;
 int    nm;
 p=stringptr(stack[fp]);
 nm=findmacro(p,0);
 delmacron(nm);
 if(nm==macroselect) macroselect=-1;

 if(whandle[MACROS])
 {
  macrosselectone(macroselect);
  refreshwindow(whandle[MACROS]);
 }
}


/* define macro */

void defmacro(int fp)
{
 char name[12];
 char defn[256];
 char * p;
 char * q;

 p=stringptr(stack[fp]);
 q=stringptr(stack[fp+1]);

 if(strlen(p)<12)
 {
  strcpy(name,p);
  if(strlen(q)<256)
  {
   strcpy(defn,q);
   if(!macrosaddwindow(name,defn,0)) zraise(ZMEM,"macro");
  }
 }
}




